package com.jsjvm.client.execution;

import java.util.Vector;

import com.jsjvm.client.classparser.JClass;
import com.jsjvm.client.classparser.JMember.JMethod;
import com.jsjvm.client.dbg.DbgUtil;
import com.jsjvm.client.execution.objrepr.ObjectRepr;
import com.jsjvm.shared.jdwp.data.JDWPLocation;

/**
 * JVM Stack: it stores every numeric value boxed, for every Category 2	value 
 * it skips an entry. Otherwise, because of dup operations, we should keep track
 * of the types at runtime. 
 * 
 * Since Java does not allow to mix between primitive and Object values in an array
 * we don't have a better option. 
 * 
 * @author ctalau
 */
public class Stack {
	private static final int KB = 1024;
	private static final int MB = 1024 * KB;
	
	protected Object [] stack = new Object [1 * MB];
	protected int top = 0;
	protected int locals = 0;
	
	/*
	 *  Push methods 
	 */
	protected Stack push(Object o){
		stack[top++] = o;
		return this;
	}
	
	protected Stack push(Object o, int skip){
		stack[top++] = o;
		top += skip;
		return this;
	}
	
	protected Stack pushi(Integer o){
		return push(o);
	}
	
	protected Stack pushl(Long o){
		return push(o, 1);
	}

	protected Stack pushf(Float o){
		return push(o);
	}

	protected Stack pushd(Double o){
		return push(o, 1);
	}

	/*
	 * Pop methods
	 */
	protected Object pop(){
		Object ret = stack[--top];
		stack[top] = null;	// throw the object to the garbage collector
		return ret;
	}
	
	// skip should be 0 and 1
	protected Object pop(int skip){
		Object ret = stack[top - 1 - skip];
		stack[top - 1] = null;
		stack[top - 1 - skip] = null;
		top = top - 1 - skip;
		return ret;
	}
	
	protected Integer popi(){
		return (Integer) pop();
	}
	
	protected Float popf(){
		return (Float) pop();
	}
	
	protected Long popl(){
		return (Long) pop(1);
	}
	
	protected Double popd(){
		return (Double) pop(1);
	}
	
	protected ObjectRepr popo(){
		return (ObjectRepr) pop();
	}
	
	/*
	 * Local variables methods
	 */
	protected void storeLocal(int off, Object o){
		stack[locals + off] = o;
	}
	
	protected Object loadLocal(int off){
		return stack[locals + off];
	}	

	// look into the stack without changing it
	protected Object peek(int off){
		return stack[top - off];
	}
	
	
	// Keep track of stack frames that need to be sent to the front-end
	private static Vector<StackFrame> frames = new Vector<StackFrame>();

	public void registerFrames(JMethod m, JClass c, int pc, int locals){
		frames.clear();
		StackFrame crt = new StackFrame(m, c, pc, locals, 0);
		while (crt != null){
			frames.add(crt);
			crt = crt.getCaller();
		}
	}
	public StackFrame getFrame(int id){
		return frames.get(id);
	}
	
	public class StackFrame {
		private JMethod m;
		private JClass c;
		private int pc;
		private int locals;		
		private int id;
		private JDWPLocation loc;
		
		public StackFrame(JMethod m, JClass c, int pc, int locals, int id) {
			super();
			this.m = m;
			this.c = c;
			this.pc = pc;
			this.locals = locals;
			this.id = id;
			this.loc = DbgUtil.createLocation(this.c, this.m, this.pc);
			Stack.frames.set(id, this);
		}

		public Object getValue(int var){
			return stack[locals + var];
		}
		public void setValue(int var, Object value){
			stack[locals + var] = value;
		}
		public ObjectRepr getThisObject(){
			if (m.isStatic())
				return null;
			return (ObjectRepr) stack[locals];
		}
		public int getID(){
			return id;
		}
		public JDWPLocation getLocation(){
			return loc;
		}
		private StackFrame getCaller(){
			int fb = this.locals + this.m.getCode().maxLocals;
			int locals = (Integer) stack[fb];
			int pc = (Integer) stack[fb+1];
			JMethod m = (JMethod) stack[fb+2];
			JClass c = (JClass) stack[fb+3];
			
			if (pc == -1)
				return null;
			
			return new StackFrame(m, c, pc, locals, id+1);
		}
	}
}


/**
 * Performance improvement:
 * 	-> Try to use javascript value representation with 64-bit tagged values
 */
